#include <iostream>
#include <ctime>
#include <Server.h>
#include <Threading.h>
#include <openssl/rc4.h>
using namespace std;

Server::Server(int pType, int pServerPort): Type(pType), server_port(pServerPort)
{
	head_client_ptr = NULL;
	tail_client_ptr = NULL;
	#ifdef WIN32
	WSADATA wsaData;
	if (WSAStartup(MAKEWORD(1, 1), &wsaData) != 0)
	{ 
		cerr << "WSAStartup failed." << endl;
		exit(1);
	}
	#endif
}

int Server::CreateSocket(int pType)			// 0 = TCP, 1 = UDP; default is to create TCP socket.
{
	if (pType == TCPSOCKET)
	{
		Type = TCPSOCKET;
		error_check = server_socket_FD = (int)socket(AF_INET, SOCK_STREAM, 0);
	}
	else
	{
		Type = UDPSOCKET;
		error_check = server_socket_FD = (int)socket(AF_INET, SOCK_DGRAM, 0);
	}
	if (error_check == -1)
	{
		cerr << "ERROR001: Creating socket of type " << pType << endl;
	}
	return error_check;
}

int Server::SetSocketOptions()					// Set socket options to reuse address.
{
	int Yes = 1;
	error_check = setsockopt(server_socket_FD, SOL_SOCKET, SO_REUSEADDR, (char*)&Yes, sizeof(int));
	if (error_check == -1)
	{
		cerr << "ERROR002: Setting socket options. " << endl;
	}
	return error_check;
}

int Server::InitialiseAddress(int pPort)	// Default Server port is 6000.
{
	server_port = pPort;
	// Server address initialisation for binding.
	server_address.sin_family = AF_INET;				// Socekt family.
	server_address.sin_addr.s_addr = htonl(INADDR_ANY);		// Setting server IP. INADDR_ANY blank IP.
	server_address.sin_port = htons(server_port);	// Setting server port.
	fill((char*)&(server_address.sin_zero), (char*)&(server_address.sin_zero)+8, '\0');
	return 0;
}

int Server::Bind()								// Bind Server socket with address.
{
	error_check = bind(server_socket_FD, (sockaddr *)&server_address, sizeof(server_address));
	if (error_check == -1)
	{
		cerr << "ERROR003: Binding." << endl;
	}
	return error_check;
}

int Server::Listen()								// Listen for incoming connections; for TCP Server.
{
	error_check = listen(server_socket_FD, 0);
	if (error_check == -1)
	{
		cerr << "ERROR004: Listening." << endl;
	}
	return error_check;
}

int Server::Accept(ClientNode* client_ptr)								// Accept incoming connections.
{
	socklen_t client_address_length = sizeof(client_ptr->client_address);
	error_check = client_ptr->client_socket_FD = (int)accept(server_socket_FD, (sockaddr*)&client_ptr->client_address, &client_address_length);
	if (error_check == -1)
	{
		cerr << "ERROR005: Accepting." << endl;
		return error_check;
	}
	cout << "*** Server got connection from " << inet_ntoa(client_ptr->client_address.sin_addr) << " on socket '" << client_ptr->client_socket_FD << "' ***" << endl;
	return error_check;
}

int Server::Receive(ClientNode* client_ptr)
{
	error_check = client_ptr->num_of_bytes_received = recv(client_ptr->client_socket_FD, client_ptr->buffer, MAXBUFFERSIZE-1, 0);
	if (error_check == -1)
	{
		cerr << "ERROR006 Receiving" << endl;
		return error_check;
	}
	client_ptr->buffer[client_ptr->num_of_bytes_received] = '\0';
	return error_check;
}

int Server::Receive(void* Data, int DataSize, ClientNode* client_ptr)
{
	error_check = client_ptr->num_of_bytes_received = recv(client_ptr->client_socket_FD, (char*)Data, DataSize, 0);
	if (error_check == -1)
	{
		cerr << "ERROR006 Receiving" << endl;
		return error_check;
	}
	return error_check;
}

int Server::Send(void* Data, int DataSize, ClientNode* client_ptr)
{
	error_check = client_ptr->num_of_bytes_sent = send(client_ptr->client_socket_FD, (char*)Data, DataSize, 0);
	if (error_check == -1)
	{
		cerr << "ERROR007: Server Sending. " << endl;
		return error_check;
	}
	return error_check;
}

int Server::CloseServerSocket()
{
	error_check = close(server_socket_FD);
	return error_check;
}

int Server::CloseClientSocket(ClientNode* client_ptr)
{
	error_check = close(client_ptr->client_socket_FD);
	return error_check;
}

// Additional Functions.
int Server::DisplayServerInfo()
{
	cout << "Server Address: " << inet_ntoa(server_address.sin_addr) << endl;
	cout << "Server Port   : " << ntohs(server_address.sin_port) << endl;
	cout << "Server Socket : " << server_socket_FD << endl;
	return 0;
}

int Server::DisplayClientInfo(ClientNode* client_ptr)
{
	cout << "Client Address: " << inet_ntoa(client_ptr->client_address.sin_addr) << endl;
	cout << "Client Port   : " << ntohs(client_ptr->client_address.sin_port) << endl;
	cout << "Client Socket : " << client_ptr->client_socket_FD << endl;
	return 0;
}

int Server::GetNumOfBytesSent(ClientNode* client_ptr)
{
	return client_ptr->num_of_bytes_sent;
}

int Server::GetNumOfBytesReceived(ClientNode* client_ptr)
{
	return client_ptr->num_of_bytes_received;
}

char* Server::GetBuffer(ClientNode* client_ptr)
{
	return client_ptr->buffer;
}

int Server::AddNewClient()
{
	ClientNode* new_client_ptr		= new ClientNode;
	ClientNode* current_client_ptr	= new ClientNode;
	
	new_client_ptr->next_client_ptr= NULL;
	Accept(new_client_ptr); //accepting and setting all the information at client node
	DisplayClientInfo(new_client_ptr); // display that information
	
	/*insertion at tail*/

	if(head_client_ptr == NULL)
	{
		new_client_ptr->previous_client_ptr = NULL;
		head_client_ptr = new_client_ptr; 
	}
	else
	{
		current_client_ptr = head_client_ptr;
		while(current_client_ptr->next_client_ptr != NULL)
			current_client_ptr = current_client_ptr->next_client_ptr;
		current_client_ptr->next_client_ptr = new_client_ptr;
	}

	new_client_ptr->server_ptr = this;
	new_client_ptr->client_thread_ID = (HANDLE)_beginthread(ThreadFunctionForClients,0,(void*)new_client_ptr);

	return 1;
}
/**Encryption Function**/
char* Encrypt(unsigned char *keydata, char *plaintext, int Length)
{
	char *encrypted = new char[Length];
	RC4_KEY key;
	RC4_set_key(&key, strlen((const char*)keydata), keydata);

	// Encryption
	RC4(&key, Length, (const unsigned char*)plaintext, (unsigned char*)encrypted);
	encrypted[Length] = '\0';

	return encrypted;
}


/** Global thread function for handling multiple clients **/
void ThreadFunctionForClients(void* ptr)
{
	ClientNode* client_data_ptr;
	client_data_ptr = (ClientNode*)ptr;

	WindowsThreading Obj;

	/** recieving **/
	int *recived_buffer,*recived_bufferi,*recived_bufferj,*recived_size;	
	client_data_ptr->server_ptr->Receive(client_data_ptr);
//	cout << "No. of Bytes received: " << client_data_ptr->server_ptr->GetNumOfBytesReceived(client_data_ptr->thread_number) << endl;
	char* buff1	 =	client_data_ptr->server_ptr->GetBuffer(client_data_ptr);
	recived_size =  (int*)buff1;
	char reply[]	= "Size received\n";
	int total_bytes	= recived_size[0];
	client_data_ptr->server_ptr->Send((void*)reply,sizeof(reply),client_data_ptr);
	recived_bufferj	= new int[total_bytes/4];
	recived_bufferi	= new int[total_bytes/4];
	int recived_bytes = 0;
	while(true)
	{
		if(client_data_ptr->server_ptr->GetNumOfBytesReceived(client_data_ptr)==-1 || recived_bytes>=total_bytes)
			break;
		client_data_ptr->server_ptr->Receive((void*)(recived_bufferi),(int)total_bytes,client_data_ptr);
//		cout<<client_data_ptr->server_ptr->GetNumOfBytesReceived(client_data_ptr->thread_number)<<endl;
		Obj.MemoryCopy((char*)(recived_bufferj)+recived_bytes,(char*)recived_bufferi,client_data_ptr->server_ptr->GetNumOfBytesReceived(client_data_ptr));
		recived_bytes += client_data_ptr->server_ptr->GetNumOfBytesReceived(client_data_ptr);
	}
	cout << "No. of Bytes received: " <<recived_bytes<< endl;
	unsigned char keydata[] = "nucesfast";
	char *Decrypted = Encrypt(keydata, (char*)recived_bufferj, total_bytes);
	recived_buffer = (int*)Decrypted;
	
	/** setting the values **/
	Obj.SetNumberOfThreads(8);
	Obj.SetRowsMatrixA(recived_buffer[0]);
	Obj.SetColumsMatrixA(recived_buffer[1]);
	Obj.SetRowsMatrixB(recived_buffer[2]);
	Obj.SetColumsMatrixB(recived_buffer[3]);	
	Obj.SetMatrixA( recived_buffer+4);
	Obj.SetMatrixB(recived_buffer + (Obj.GetRowsMatrixA())*(Obj.GetColumsMatrixA()) + 4);
	
	/** creating data structure for threading **/
	DataForThreadFunc *data_for_thread_func = new DataForThreadFunc[Obj.GetNumberOfThreads()]; //object=number of threads
	for(int i=0; i<Obj.GetNumberOfThreads(); i++)
	{
		data_for_thread_func[i].current_object = &Obj;	//assigning same object address to all data structure
		data_for_thread_func[i].thread_number = 0;		//intializing
	}
	
	/** Dynamically Assigning memory **/
	Obj.AssignMemory(MATRIXC);
	Obj.AssignMemory(MATRIXBTRANSPOSE);

//	time_t start_time[MAXNUMBEROFCLIENTS], end_time[MAXNUMBEROFCLIENTS], time_difference[MAXNUMBEROFCLIENTS];

	/** creating dynamic thread array **/
	HANDLE* thread_array;
	thread_array = new HANDLE[Obj.GetNumberOfThreads()];

//	start_time[client_data_ptr] = clock();
	
	Obj.MatrixTranspose(); //B transpose
	for(int thread_counter=0; thread_counter<Obj.GetNumberOfThreads(); thread_counter++)
	{	
		data_for_thread_func[thread_counter].thread_number = thread_counter;
		thread_array[thread_counter] = (HANDLE)_beginthread(ThreadFunction,0,(void*)&data_for_thread_func[thread_counter]);
	}
	for (int thread_counter=0; thread_counter<Obj.GetNumberOfThreads(); thread_counter++)
	WaitForSingleObject(thread_array[thread_counter], INFINITE);
//	end_time[client_data_ptr] = clock();
//	time_difference[client_data_ptr] = end_time[client_data_ptr] - start_time[client_data_ptr];
	
//	cout << "Time taken =  "<< time_difference[client_data_ptr] <<"(msec)"<< endl;

	/** Sending**/
	cout<<"Start Sending result"<<endl;
	int	matrixC_length=Obj.GetColumsMatrixB()* Obj.GetRowsMatrixA();
	unsigned char *matrix_C_Buffer= new unsigned char[sizeof(int)*matrixC_length];
	memcpy(matrix_C_Buffer, Obj.GetMatrixC(), sizeof(int)*matrixC_length);
	int totalbytes1=matrixC_length*4;
	int sentbytes=0;
	char *Encrypted = Encrypt(keydata, (char*)matrix_C_Buffer, totalbytes1);
	while(true)
	{
		if(client_data_ptr->server_ptr->GetNumOfBytesSent(client_data_ptr)==-1 || sentbytes>=totalbytes1)
			break;
		client_data_ptr->server_ptr->Send((void*)(Encrypted+sentbytes),totalbytes1,client_data_ptr);//sending matrix 
		sentbytes+=client_data_ptr->server_ptr->GetNumOfBytesSent(client_data_ptr);
	}
	cout<<"Sending done"<<endl;

	/** Storing in files **/
//	Obj.MatrixStore(MATRIXA);
//	Obj.MatrixStore(MATRIXB);
//	Obj.MatrixStore(MATRIXC);

	client_data_ptr->server_ptr->CloseClientSocket(client_data_ptr); // Close sockets.
}